﻿#ifndef SH_BUFFER_STREAMBUFFER_HPP
#define SH_BUFFER_STREAMBUFFER_HPP

#include "buffer.hpp"
#include "../endian.hpp"

namespace sh {

class StreamBuffer : public Buffer
{
public:
	using Buffer::Buffer;
	StreamBuffer() noexcept;
	StreamBuffer(ByteOrder order);
	StreamBuffer(Buffer const& rhs);
	StreamBuffer(Buffer&& rhs) noexcept;
	StreamBuffer(StreamBuffer const& rhs);
	StreamBuffer(StreamBuffer&& rhs) noexcept;
	StreamBuffer& operator=(Buffer const& rhs);
	StreamBuffer& operator=(Buffer&& rhs) noexcept;
	StreamBuffer& operator=(StreamBuffer const& rhs);
	StreamBuffer& operator=(StreamBuffer&& rhs) noexcept;
	StreamBuffer& operator=(std::initializer_list<value_type> list);
	~StreamBuffer();

	void swap(StreamBuffer& rhs);

	void setByteOrder(ByteOrder order);
	sh::ByteOrder byteOrder() const;

	
	// 写入
	template<typename T> // 基本数据类型
	typename std::enable_if<std::is_arithmetic<T>::value, StreamBuffer&>::type
	operator << (T&& val);
	template<typename T> // value_type || Buffer
	typename std::enable_if<std::is_same<T, value_type>::value || std::is_same<T, Buffer>::value, StreamBuffer&>::type
	operator << (T&& val) { Buffer::append(val); }
	template<typename T> void append(T&& val);

	// 读出
	template<typename T> // 基本数据类型
	typename std::enable_if<std::is_arithmetic<T>::value, StreamBuffer&>::type
	operator >> (T& val);
	template<typename T> T read();

private:
	sh::DataConvert convert_;
};


/*
* //////////////////////////////////////////////////////////////////////////////////////////
*
*
*
* //////////////////////////////////////////////////////////////////////////////////////////
*/






//template<typename T>
//inline StreamBuffer& StreamBuffer::operator << (T&& val) {
//	constexpr size_t Size = sizeof(T);
//	Buffer tmpBuffer(Size, 0);
//	convert_.data2hex(val, tmpBuffer.data(), 0, Size);
//	Buffer::append(tmpBuffer);
//	return *this;
//}

template<typename T>
inline 
typename std::enable_if<std::is_arithmetic<T>::value, StreamBuffer&>::type
StreamBuffer::operator << (T&& val) {
	constexpr size_t Size = sizeof(T);
	Buffer tmpBuffer(Size, 0);
	convert_.data2hex(val, tmpBuffer.data(), 0, Size);
	Buffer::append(tmpBuffer);
	return *this;
}

template<typename T>
inline void StreamBuffer::append(T&& val) {
	(*this) << val;
}




template<typename T>
inline 
typename std::enable_if<std::is_arithmetic<T>::value, StreamBuffer&>::type
StreamBuffer::operator >> (T& val) {
	constexpr size_t Size = sizeof(T);
	if(size() < Size) throw "cross the border";
	convert_.hex2data(val, this->data(), 0, Size);
	remove(0, Size);
	return *this;
}

template<typename T>
inline T StreamBuffer::read() {
	T val{};
	(*this) >> val;
	return val;
}


inline
StreamBuffer::StreamBuffer() noexcept
	: Buffer()
{
}

inline
StreamBuffer::StreamBuffer(ByteOrder order)
	: StreamBuffer()
{
	convert_.setDefaultEndian(order);
}

inline
StreamBuffer::StreamBuffer(Buffer const& rhs)
	: Buffer(rhs)
{

}

inline
StreamBuffer::StreamBuffer(Buffer&& rhs) noexcept
	: Buffer(std::forward<Buffer>(rhs))
{

}

inline
StreamBuffer::StreamBuffer(StreamBuffer const& rhs)
{
	buffer_ = rhs.buffer_;
	convert_ = rhs.convert_;
}

inline
StreamBuffer::StreamBuffer(StreamBuffer&& rhs) noexcept
{
	buffer_.swap(rhs.buffer_);
	convert_ = rhs.convert_;
}

inline
StreamBuffer& StreamBuffer::operator=(Buffer const& rhs)
{
	//buffer_ = rhs; // qt for android error
	buffer_  = (buffer_type)rhs;
	return *this;
}

inline
StreamBuffer& StreamBuffer::operator=(Buffer&& rhs) noexcept
{
	Buffer::swap(rhs);
	return *this;
}

inline
StreamBuffer& StreamBuffer::operator=(StreamBuffer const& rhs)
{
	buffer_ = rhs.buffer_;
	convert_ = rhs.convert_;
	return *this;
}

inline
StreamBuffer& StreamBuffer::operator=(StreamBuffer&& rhs) noexcept
{
	buffer_.swap(rhs.buffer_);
	convert_ = rhs.convert_;
	return *this;
}

inline
StreamBuffer& StreamBuffer::operator=(std::initializer_list<value_type> list)
{
	buffer_ = list;
	return *this;
}

inline
StreamBuffer::~StreamBuffer()
{
}

inline
void StreamBuffer::swap(StreamBuffer& rhs)
{
	Buffer::swap(rhs);
	std::swap(convert_, rhs.convert_);
}

inline
void StreamBuffer::setByteOrder(sh::ByteOrder order)
{
	convert_.setDefaultEndian(order);
}

inline
sh::ByteOrder StreamBuffer::byteOrder() const
{
	return convert_.getDefaultEndian();
}





} // namespace sh




//#ifdef SH_ONLY_HRADER
//#include "detail/streambuffer_impl.hpp"
//#endif //  SH_ONLY_HRADER

#endif // SH_BUFFER_STREAMBUFFER_HPP
